//#include "xSceneModel.h"
#include "xSceneView.h"
#include <QGridLayout>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers>
#include <osg/ComputeBoundsVisitor>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgUtil/LineSegmentIntersector>
#include "xSceneModel.h"
#include "xSceneManger.h"
#include "selfDefTerrainManipulator.h"
#include "selfDefTrackBallManipulator.h"
#include "qcomm.h"
#include <osg/ComputeBoundsVisitor>
#include <QStandardItem>
#include "iosgViewerEvent.h"
#include "iosgMeasureEvent.h"
#include "compass.h"
#include "selfDefTerrainManipulator.h"
#include "qcomm.h"
#include "IUserEventCallBack.h"
#include "BaseUserEvent.h"

const unsigned int defaultRefreshPeriod = 2;

//------------------------------------------------------------------------------------------------------------

xSceneView::xSceneView(QWidget *parent, IEventCall *pCallback)
    : QWidget(parent),
      m_refreshPeriod(defaultRefreshPeriod),
      m_pCall(pCallback)
{
    setThreadingModel(osgViewer::Viewer::SingleThreaded);
	setKeyEventSetsDone(0);

    //初始化数据层
    m_pModel = new xSceneModel(parent);

    //初始化窗口!
    double left = 0;
    double right = 100;
    double bottom = 0;
    double top = 100;
    double width = right - left;
    double height = top - bottom;

    QWidget* widget = addViewWidget(createGraphicsWindow(left, bottom, width, height), NULL);

    //增加指南针！
    double compassX = right - 1.5 * height / 8;
    double compassY = bottom - 1.2 * height / 8;
    double compassWidth = height / 8;
    double compassHeight = height / 8;

    osg::Group* ptrRoot = m_pModel->getRootNode();
    osg::Group* mViewerGroup = ptrRoot->getChild(2)->asGroup();

    createCompass(m_pView, mViewerGroup, compassX, compassY, compassHeight, compassWidth);

	QGridLayout* grid = new QGridLayout;

    slotUpdateModel();
    connect(m_pModel, SIGNAL(sigUpdateModel()), this, SLOT(slotUpdateModel()), Qt::UniqueConnection);

	// maximal area for widget
    grid->setContentsMargins(1, 1, 1, 1);
	grid->setSpacing(0);
	grid->addWidget(widget, 0, 0);
	setLayout(grid);

    connect(&m_timer, SIGNAL(timeout()), this, SLOT(update()));
    m_timer.start(m_refreshPeriod);

    //初始化管理层
    m_pManager = xSceneManger::getInstance(ptrRoot);
}

void xSceneView::openScene(const std::string fileName)
{
    m_pManager->loadScene(fileName);

    static bool bIsFirstTime = false;

    if(bIsFirstTime == false)
    {
        //第一次打开!
        setLightEnable();
        bIsFirstTime = true;
    }

    slotUpdateModel();
    sigAddFileToTree(QString::fromStdString(fileName));
}

void xSceneView::unloadAll()
{
    m_pManager->unloadAll();
    sigRemoveFileFromTree();
}


QWidget* xSceneView::addViewWidget(osgQt::GraphicsWindowQt* gw, osg::Node* scene)
{
    initSceneGraph();
    initManipulator();
    initCamera(gw);
    initEvent();

	return gw->getGLWidget();
}

osgQt::GraphicsWindowQt* xSceneView::createGraphicsWindow( int x, int y, int w, int h, const std::string& name, bool windowDecoration)
{
	osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
	osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
	traits->windowName = name;
	traits->windowDecoration = windowDecoration;
	traits->x = x;
	traits->y = y;
	traits->width = w;
	traits->height = h;
	traits->doubleBuffer = true;
	traits->alpha = ds->getMinimumNumAlphaBits();
	traits->stencil = ds->getMinimumNumStencilBits();
	traits->sampleBuffers = ds->getMultiSamples();
	traits->samples = ds->getNumMultiSamples();

    return new CUserDefineGraphicsWindowQt(traits.get());
}

void xSceneView::paintEvent(QPaintEvent *event)
{
    frame();
}

void xSceneView::initManipulator()
{
	// create a Manipulator Switcher
	osg::ref_ptr<CSelfDefTerrainManipulator> terrain = new CSelfDefTerrainManipulator();
	osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;

    // Add our trackball manipulator to the switcher
    keyswitchManipulator->addMatrixManipulator('1', "Terrain", terrain.get());

	// Init the switcher to the first manipulator (in this case the only manipulator)
    keyswitchManipulator->selectMatrixManipulator(0);  // Zero based index Value

    m_pView->setCameraManipulator(keyswitchManipulator);
}

void xSceneView::initEvent()
{	
    m_pView->addEventHandler(new osgViewer::StatsHandler);
    m_pView->addEventHandler(new osgGA::StateSetManipulator(m_pView->getCamera()->getOrCreateStateSet()));

    //初始化浏览器事件!
    osg::Group* ptrRoot = m_pModel->getRootNode();
    IOsgViewerEventFactory iveFactory;
    std::auto_ptr<IOsgViewerEvent> pIVEvent(iveFactory.create(m_pView, ptrRoot));
    pIVEvent->addEventToViewer(m_pView);

    IOsgMeasureEventFactory imeFactory;
    std::auto_ptr<IOsgMeasureEvent> pIMEvent(imeFactory.create(m_pView, ptrRoot, m_pCall));
    pIMEvent->addEventToViewer(m_pView);
}

void xSceneView::initCamera(osgQt::GraphicsWindowQt *gw)
{
    m_pCamera = m_pView->getCamera();
    m_pCamera->setGraphicsContext(gw);

    const osg::GraphicsContext::Traits* traits = gw->getTraits();

    m_pCamera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
    m_pCamera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
    m_pCamera->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width) / static_cast<double>(traits->height), 1.0f, 10000.0f);
    gw->setTouchEventsEnabled(true);
}

void xSceneView::initSceneGraph()
{
    m_pView = new osgViewer::View;
    addView(m_pView.get());

    osg::ref_ptr<osg::Group> ptrRoot = m_pModel->getRootNode();
    m_pView->setSceneData(ptrRoot.get());
}

void xSceneView::home() const
{
    m_pView->home();
}

void xSceneView::setLightEnable() const
{
    m_pView->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_L);
}

void xSceneView::setTextureEnable() const
{
    m_pView->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_W);
}

void xSceneView::frontView() const
{
    //计算旋转!
    const double angle1 = 90;
    const osg::Vec3d axis1 = osg::Vec3(1., 0., 0.);
    const osg::Matrix rot1 = calRotate(angle1, axis1);

    const double angle2 = 0;
    const osg::Vec3d axis2 = osg::Vec3(0, 0., 1.);
    const osg::Matrix rot2 = calRotate(angle2, axis2);

    osg::Matrix rot = rot1 * rot2;

    //计算位移!
    const osg::Vec3d dxyz = osg::Vec3d(0, -2, 0);
    osg::Matrix trans;
    bool bIsSuccess = calTrans(dxyz, trans);

    if(!bIsSuccess)
    {
        return;
    }

    //改变视图!
    changeView(rot, trans);
}

void xSceneView::rearView() const
{
    const double angle1 = 90;
    const osg::Vec3d axis1 = osg::Vec3(1., 0., 0.);
    const osg::Matrix rot1 = calRotate(angle1, axis1);

    const double angle2 = 180;
    const osg::Vec3d axis2 = osg::Vec3(0, 0., 1.);
    const osg::Matrix rot2 = calRotate(angle2, axis2);

    osg::Matrix rot = rot1 * rot2;

    //计算位移!
    const osg::Vec3d dxyz = osg::Vec3d(0, 2, 0);
    osg::Matrix trans;
    bool bIsSuccess = calTrans(dxyz, trans);

    if(!bIsSuccess)
    {
        return;
    }

    //改变视图!
    changeView(rot, trans);
}

void xSceneView::leftView() const
{
    const double angle1 = 90;
    const osg::Vec3d axis1 = osg::Vec3(0., 0., -1.);
    const osg::Matrix rot1 = calRotate(angle1, axis1);

    const double angle2 = 90;
    const osg::Vec3d axis2 = osg::Vec3(0, -1., 0.);
    const osg::Matrix rot2 = calRotate(angle2, axis2);

    osg::Matrix rot = rot1 * rot2;

    //计算位移!
    const osg::Vec3d dxyz = osg::Vec3d(-2, 0, 0);
    osg::Matrix trans;
    bool bIsSuccess = calTrans(dxyz, trans);

    if(!bIsSuccess)
    {
        return;
    }

    //改变视图!
    changeView(rot, trans);
}

void xSceneView::rightView() const
{
    const double angle1 = 90;
    const osg::Vec3d axis1 = osg::Vec3(1., 0., 0.);
    const osg::Matrix rot1 = calRotate(angle1, axis1);

    const double angle2 = 90;
    const osg::Vec3d axis2 = osg::Vec3(0, 0., 1.);
    const osg::Matrix rot2 = calRotate(angle2, axis2);

    osg::Matrix rot = rot1 * rot2;

    //计算位移!
    const osg::Vec3d dxyz = osg::Vec3d(2, 0, 0);
    osg::Matrix trans;
    bool bIsSuccess = calTrans(dxyz, trans);

    if(!bIsSuccess)
    {
        return;
    }

    //改变视图!
    changeView(rot, trans);
}

void xSceneView::lookUpView() const
{
    const double angle = 180;
    const osg::Vec3d axis = osg::Vec3(1., 0., 0.);
    const osg::Matrix rot = calRotate(angle, axis);

    //计算位移!
    const osg::Vec3d dxyz = osg::Vec3d(0, 0, -2);
    osg::Matrix trans;
    bool bIsSuccess = calTrans(dxyz, trans);

    if(!bIsSuccess)
    {
        return;
    }

    //改变视图!
    changeView(rot, trans);
}

void xSceneView::lookDownView() const
{
    const double angle = 0;
    const osg::Vec3d axis = osg::Vec3(1., 0., 0.);
    const osg::Matrix rot = calRotate(angle, axis);

    //计算位移!
    const osg::Vec3d dxyz = osg::Vec3d(0, 0, 2);
    osg::Matrix trans;
    bool bIsSuccess = calTrans(dxyz, trans);

    if(!bIsSuccess)
    {
        return;
    }

    //改变视图!
    changeView(rot, trans);
}

void xSceneView::updateActivatedStatus(const std::vector<OperatingState> vecOps) const
{
    for(osgViewer::View::EventHandlers::iterator hitr = m_pView->getEventHandlers().begin(); hitr != m_pView->getEventHandlers().end(); ++hitr)
    {
        osgGA::EventHandler* pEvent = *hitr;

        osg::ref_ptr<CBaseUserEvent> ph = dynamic_cast<CBaseUserEvent*> (pEvent);

        if (!ph)
        {
            continue;
        }

        ph->updateActivatedStatus(vecOps);
    }
}

void xSceneView::changeView(const osg::Matrix rot, const osg::Matrix trans) const
{
    // 设置视口矩阵（旋转矩阵和平移矩阵连乘）!
    m_pView->getCameraManipulator()->setByMatrix(rot * trans);
}

const osg::Matrix xSceneView::calRotate(const double angle, const osg::Vec3d axis) const
{
    osg::Matrix rot;
    rot.makeRotate(osg::DegreesToRadians(angle), axis);
    return rot;
}

bool xSceneView::calTrans(const osg::Vec3d dxyz, osg::Matrix &trans) const
{
    const osg::Vec3d xyz = pickTransPt(dxyz);

    if(xyz.x() == 0 && xyz.y() == 0 && xyz.z() == 0)
    {
        return false;
    }

    trans.makeTranslate(xyz.x(), xyz.y(), xyz.z());
    return true;
}


const osg::Vec3d xSceneView::pickTransPt(const osg::Vec3d dxyz) const
{
    osg::Group* m_pRoot = m_pModel->getRootNode();
    osg::ref_ptr<osg::Node> node = m_pRoot->getChild(0);

    if (!node)
    {
        return osg::Vec3d(0, 0, 0);
    }

    osg::ComputeBoundsVisitor cbbv;
    node->accept(cbbv);
    osg::BoundingBox bb = cbbv.getBoundingBox();
    osg::Vec3f bp = bb.center();
    double radius = bb.radius();

    const osg::Vec3d start(bp.x(), bp.y(), bp.z() + radius);
    const osg::Vec3d end(bp.x(), bp.y(), bp.z() - radius);

    osg::ref_ptr<osgUtil::LineSegmentIntersector> ls = new osgUtil::LineSegmentIntersector(start, end);
    // 创建一个IV!
    osgUtil::IntersectionVisitor iv(ls);

    // 把线段添加到IV中!
    node->accept(iv);

    if (ls.valid() && ls->containsIntersections())
    {
        osg::Vec3d center = ls->getFirstIntersection().getWorldIntersectPoint();
        osg::Vec3d transPt = center + dxyz * radius;
        return transPt;
    }
    else
    {
        return osg::Vec3d(0, 0, 0);
    }
}


void xSceneView::slotUpdateModel()
{
    if (m_pModel == NULL)
        return;

    osg::ref_ptr<osg::Group> ptrRoot = m_pModel->getRootNode();
    osg::ref_ptr<osg::Node> sceneGroup = ptrRoot->getChild(0);
    m_pView->setSceneData(ptrRoot);

    if (ptrRoot.valid())
    {
        if(sceneGroup->asGroup()->getNumChildren() == 0)
        {
            return;
        }

        osg::ref_ptr<osg::Node> node = sceneGroup->asGroup()->getChild(0);
        osg::ComputeBoundsVisitor cbbv;
        node->accept(cbbv);
        osg::BoundingBox bb = cbbv.getBoundingBox();
        osg::Vec3d homeCenter = bb.center() + osg::Vec3( 1.5f * bb.radius(),1.5f * bb.radius(),1.5f * bb.radius());

        m_pView->getCameraManipulator()->setHomePosition(homeCenter,
            bb.center(), osg::Vec3(0.0f,0.0f,1.0f));
        m_pView->home();
    }
}

void xSceneView::slotCheckNode(QStandardItem *item)
{
    QStandardItem* parent = item->parent();

    if(!parent)
    {
        return;
    }

    QString qParentStr = parent->text();
    QString qChilStr = item->text();
    QString qStr = qParentStr.append(qChilStr);
    m_pManager->checkScene(qStr.toStdString());
}


